#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <sys/time.h>
#include "athread.h"

double ex[NX][NY+1];
double ey[NX+1][NY];
double hz[NX][NY];

//for check 
double ex1[NX][NY+1]; 
double ey1[NX+1][NY]; 
double hz1[NX][NY];


int iter, N, N_loop1; ////n is the num of tiles that each thread assigned
int thread_nums,ntiley, ntilex;
int loop1_startj[64];
int startj[64];
int starti[64];
int reuse_num;
#define my_ceil(a,b) \
     (a%b==0)?a/b:a/b+1

extern SLAVE_FUN(func)();

void init_array(double ex[][NY+1],double ey[][NY], double hz[][NY])
{
   int i, j;
   for(i = 0; i < NX+1; i++)
     for(j = 0; j < NY; j++)
        ey[i][j] = (double)((i+1)/(j+1));

   for(i = 0; i < NX; i++)
     for(j = 0; j < NY+1; j++)
        ex[i][j] =(double)(1/(i+j+1)); 


   for(i = 0; i < NX; i++)
     for(j = 0; j < NY; j++)
     {
        hz[i][j] = (double)(1/(i+1)/(j+1));
     }
        
   
}

int check()
{
  int i1,j1,t1;
  //init
  init_array(ex1,ey1,hz1); 
  //compute
  for(t1=0; t1<TMAX; t1++)
  {
//    for(j1=0; j1<NY; j1++)
//      ey1[0][j1] = t1;
    for(i1=1; i1<NX; i1++)
      for(j1=0; j1<NY; j1++)
        ey1[i1][j1] = ey1[i1][j1] - 0.5*(hz1[i1][j1]-hz1[i1-1][j1]);
    for(i1=0; i1<NX; i1++)
      for(j1=1; j1<NY; j1++)
        ex1[i1][j1] = ex1[i1][j1] - 0.5*(hz1[i1][j1]-hz1[i1][j1-1]);
    for(i1=0; i1<NX; i1++)
      for (j1=0; j1<NY; j1++)
      {
        //printf("%f-0.7*(%f-%f+%f-%f)\t",hz1[i1][j1],ex1[i1][j1+1],ex1[i1][j1],ey1[i1+1][j1],ey1[i1][j1]);
        hz1[i1][j1]=hz1[i1][j1]-0.7*(ex1[i1][j1+1]-ex1[i1][j1]+ey1[i1+1][j1]-ey1[i1][j1]);
        //printf("%f\n",hz1[i1][j1]);
      }
      
  }
  //check
  //loop1_ey
  for(j1=0; j1<NY; j1++) 
  {
     if(fabs(ey1[0][j1]-ey[0][j1])>1e-10)
     {
        printf("loop1-ey<%d,%d >: ",0,j1);
        printf("master=%f is incoistent with ",ey1[0][j1]);
        printf("slave=%f\n",ey[0][j1]);
        return 1;
     }
  }
  //loop2_ey
  for(i1=1; i1<NX; i1++)
    for(j1=0; j1<NY; j1++)
    {
      if(fabs(ey1[i1][j1]-ey[i1][j1])>1e-10)
      {   
        printf("loop2-ey<%d,%d >: ",i1,j1);
        printf("master=%f is incoistent with ",ey1[i1][j1]);
        printf("slave=%f\n",ey[i1][j1]);
        return 1;
      } 
    }
 //loop3_ex
  for(i1=0; i1<NX; i1++)
    for(j1=1; j1<NY; j1++)  
    {
      if(fabs(ex1[i1][j1]-ex[i1][j1])>1e-10)
      {   
        printf("loop3-ex<%d,%d >: ",i1,j1);
        printf("master=%f is incoistent with ",ex1[i1][j1]);
        printf("slave=%f\n",ex[i1][j1]);
        return 1;
      }
    }
  //loop4_hz
  for(i1=0;i1<NX;i1++)
   for(j1=0;j1<NY;j1++)
   {
     if(fabs(hz1[i1][j1]-hz[i1][j1])>1e-10)
     {
       printf("< %d,%d >: ",i1,j1);
       printf("%f is incoistent with ",hz1[i1][j1]);
       printf("%f\n",hz[i1][j1]);
       return 1;
     }	
   }
   printf("......check is right !......\n");
   return 0;
}


int main(int argc, char** argv)
{
  int i,j,k,t;
  struct timeval start;
  struct timeval end;
  double total_time;    
  if(argc>1)
    thread_nums =atoi(argv[1]); 
  init_array(ex,ey,hz);
  printf("iter=%d,thread_nums=%d\n", TMAX,thread_nums);
  athread_init();
  {
    int id;//thread id;
    int task_num,loop1_task_num;
    id = 0;
    N_loop1 = (((NY)/TZ2))/thread_nums;
    N = (((NY)/TZ2)*((NX)/TZ1))/thread_nums;
    ntiley=my_ceil((NY),TZ2);
    ntilex=my_ceil((NX),TZ1);
    //init the start addr of task  
    task_num=0;
    loop1_task_num=0;
    loop1_startj[0]=0;
    starti[0] = 0;
    startj[0] = 0;
    for(i=0,j=0;j<NY-TZ2+1;j+=TZ2)
    {
        ++loop1_task_num;
        if(loop1_task_num==N_loop1+1)
        {
            ++id;
            loop1_startj[id] = j;
            loop1_task_num=1;
        }
    }
    id=0;
    for(i = 0; i < (NX)-TZ1+1; i += TZ1)
      for(j = 0; j < (NY)-TZ2+1; j += TZ2)
      {   
         ++task_num;
         //++loop1_task_num;
         if(task_num == N+1)
         {   
           ++id;
           starti[id] = i;
           startj[id] = j;
           //printf("starti[%d]=%d\n",id,starti[id]);
           //printf("startj[%d]=%d\n",id,startj[id]);
           task_num=1;
           
         }   
      }
  }//finish partition tiles
  gettimeofday(&start,NULL);
  for(iter=0; iter<TMAX; iter++)
  {
    athread_spawn(func,0); 
    athread_join();
  }
  gettimeofday(&end,NULL);
  athread_halt();
     
  total_time = 0.0;
  total_time+=(end.tv_sec-start.tv_sec)+(end.tv_usec-start.tv_usec)/1000000.0;
  printf("total_time: %lf\n",total_time);     
#ifdef CHECK
  if(check())
  {
     printf(" error\n");
     exit(1);
  }
#endif
  return 0;
}
         
